#include "DirInfo.h"
#include "FileSystem.h"
#include <errno.h>
#include <stdlib.h>
#include <iostream>


namespace FileSystem
{

bool InnerIsMatch(std::string const &s, std::string const &mask, uint from_s, uint from_mask)
{
	uint i, j;
	bool let_eq = true;
	for (i=from_mask, j = from_s; i<mask.size() && j<s.size(); ++i, ++j)
	{
		switch (mask[i])
		{
		case '?':
			
			break;
		case '*':
			if (i == mask.size() - 1)
				return true;

			for (uint j1=j; j1<s.size(); ++j1)
			{
				if (InnerIsMatch(s, mask, j1, i+1))
					return true;
			}
			return false;
			break;
		default:
			if (mask[i] != s[j])
				return false;
			break;
		}
	}

	if ((i != mask.size()) || (j != s.size()))
	{
		return false;
	} else {
		return true;
	}
}

bool IsMatch(std::string const &s, std::string const &mask)
{
	return InnerIsMatch(s, mask, 0, 0);
}



DirInfo::DirInfo(const std::string &fullName) :
	FileSystemInfo(fullName, true)
{
}

DirInfo::~DirInfo(void)
{
	
}

std::vector<DirInfo> DirInfo::GetDirs(const std::string &mask) const
{
	vector<DirInfo> dirs;

#ifdef WINDOWS

	std::string m = Combine(FileSystemInfo::GetFullName(), mask);
	
	_finddata_t fd;
	uintptr_t handle = _findfirst(m.c_str(), &fd);
	if (-1 == handle)
		return dirs;

	do
	{
		if ((0 == (_A_SUBDIR & fd.attrib)) ||
				(0 == strcmp("..", fd.name)) ||
				(0 == strcmp(".", fd.name)))
		{
			continue;
		}

		std::string fdname(fd.name);
		if (!IsMatch(fdname, mask))
			continue;
		std::string s = Combine(GetFullName(), fdname);
		DirInfo dirInfo(s);
		dirs.push_back(dirInfo);

	} while (0 == _findnext(handle, &fd));
	_findclose(handle);

#else

	std::string m = GetFullName();
	if (m.size() == 0)
		m = ".";
	
	DIR *dp;
	struct dirent *dirp;

	//cout << "-------------------------------------------" << endl;
	//cout << "GetDirs():" << endl;	
	//cout << m << endl;

	dp = opendir(m.c_str());
	if (0 == dp)
		return dirs;

	while (0 != (dirp = readdir(dp)))
	{
		std::string dpname = Combine(GetFullName(), string(dirp->d_name));

		struct stat inf;
		if (-1 == stat(dpname.c_str(), &inf))
		{
			perror(dpname.c_str());
			throw EXC_CANNOT_OPEN_DIR;
		}

		//cout << dirp->d_name << "|\t|" << inf.st_mode << "|\t|" << (inf.st_mode & S_IFMT) << endl;

		if ((S_IFDIR != (S_IFMT & inf.st_mode)) ||
			(0 == strcmp("..", dirp->d_name)) ||
			(0 == strcmp(".", dirp->d_name)))
		{
			continue;
		}

		std::string fdname(dirp->d_name);
		if (!IsMatch(fdname, mask))
			continue;
		std::string s = Combine(GetFullName(), fdname);
		DirInfo dirInfo(s);
		dirs.push_back(dirInfo);
	}

	closedir(dp);

#endif

	//cout << dirs.size() << endl;

	return dirs;
}

std::vector<FileInfo> DirInfo::GetFiles(const std::string &mask) const
{
	std::vector<FileInfo> files;

#ifdef WINDOWS

	std::string m = Combine(FileSystemInfo::GetFullName(), mask);

	_finddata_t fd;
	uintptr_t handle = _findfirst(m.c_str(), &fd);
	if (-1 == handle)
		return files;

	do
	{
		if ((0 != (_A_SUBDIR & fd.attrib)) ||
				(0 == strcmp("..", fd.name)) ||
				(0 == strcmp(".", fd.name)))
		{
			continue;
		}

		std::string fdname(fd.name);
		if (!IsMatch(fdname, mask))
			continue;
		std::string s = Combine(GetFullName(), fdname);
		FileInfo fileInfo(s);
		files.push_back(fileInfo);
	} while (0 == _findnext(handle, &fd));
	_findclose(handle);

#else
	
	std::string m = GetFullName();
	if (m.size() == 0)
		m = ".";

	//cout << "-------------------------------------------" << endl;
        //cout << "GetFiles():" << endl;
        //cout << m << endl;

	DIR *dp;
	struct dirent *dirp;
	
	dp = opendir(m.c_str());
	if (0 == dp)
		return files;

	while (0 != (dirp = readdir(dp)))
	{
		std::string dpname = Combine(GetFullName(), string(dirp->d_name));

		struct stat inf;
		if (-1 == stat(dpname.c_str(), &inf))
		{
			perror(dpname.c_str());
			throw EXC_CANNOT_OPEN_DIR;
		}

                //cout << dirp->d_name << "|\t|" << inf.st_mode << "|\t|" << (inf.st_mode & S_IFMT) << endl;



		if ((S_IFDIR == (S_IFMT & inf.st_mode)) ||
				(0 == strcmp("..", dirp->d_name)) ||
				(0 == strcmp(".", dirp->d_name)))
		{
			continue;
		}

		std::string fdname(dirp->d_name);
		if (!IsMatch(fdname, mask))
			continue;
		std::string s = Combine(GetFullName(), fdname);
		FileInfo fileInfo(s);
		files.push_back(fileInfo);
	}

	closedir(dp);


#endif

	//cout << files.size() << endl;

	return files;
}

bool DirInfo::ExistsDir(const string &name) const
{
	std::vector<DirInfo> dirs = GetDirs(name);
	for (uint i=0; i<dirs.size(); ++i)
	{
		if (name == dirs[i].GetName())
			return true;
	}
	return false;
}

bool DirInfo::ExistsFile(const std::string &name) const
{
	std::vector<FileInfo> files = GetFiles(name);
	for (uint i=0; i<files.size(); ++i)
	{
		if (name == files[i].GetName())
			return true;
	}
	return false;
}

void DirInfo::CreateDir(const std::string &name)
{
	std::string dirName = Combine(GetFullName(), name).c_str();

#ifdef WINDOWS

	if ((0 != _mkdir(dirName.c_str())) &&
		(errno != EEXIST))
	{
		throw EXC_CANNOT_CREATE_DIR;
	}
	
#else
	std::string cmd = std::string("mkdir \"") + dirName + string("\"\n");
	if (0 != system(0)) // test /bin/sh
	{
		//cout << "test /bin/sh failed" << endl;
		//throw EXC_CANNOT_CREATE_DIR;
	}
	

	cout << cmd.c_str() << endl;
	if (0 != system(cmd.c_str()))
	{
		cout << cmd.c_str() << " failed" << endl;
		throw EXC_CANNOT_CREATE_DIR;
	}

#endif

}

}
